Chart Explainer and Conversations
This plugin is currently experimental.
Important
- This tutorial requires the Chart Explainer and Conversations plugin as well as the CLI plugin.
- By default, this plugin connects to an LLM service provided by FinTech Studios (FTS). Instructions for setting up a proxy authentication server to access the LLM is detailed in the API Client section.
- To run the proxy server, you will need an FTS account and API key. You can sign up for an account at https://apollo.fintechstudios.com/. To obtain an API key, contact support@fintechstudios.com after you have registered.
Introduction
An AI-powered plugin that combines Chart Explainer and Conversational capabilities. Users can manipulate charts using natural language commands and query for explanations, news, and analysis. The conversational interface operates independently or alongside the existing Chart Explainer feature, which generates AI-powered summaries of market events for selected dates or date ranges on the ticker. See the Core Settings section below for instructions on enabling or disabling either.
ChartIQ CLI Integration
The plugin integrates with the ChartIQ CLI plugin, allowing the LLM to execute CLI commands as part of the conversation. This means the AI can dynamically modify the chart by changing its type, adjusting periodicity or range, adding or editing studies and series, extracting chart data for analysis, resetting the chart, and more, all based on the conversation context.
For more details on the CLI and available commands, see the CLI Tutorial.
Getting Started
Import both the CLI and Chart Explainer plugins into your application:
import "./plugins/cli/cli.js"; // @@ COMMAND_LINE
import { addPluginConfig } from './plugins/chartexplainer/plugin'; // @@ FTS_CHART_EXPLAINER
Configuration Options
You will find most of the following settings are pre-configured in defaultConfiguration.js at plugins.ftsChartExplainer.ftsConfig. Some configurations are available but not configured out of the box.
Core Settings
hasNewsPackage(boolean)- Enables the Chart Explainer feature introduced in v9.6.0, which generates AI-driven news summaries for selected date ranges. When enabled, users can select a date or date range on the chart, and the plugin aggregates news stories from tokened sources into a bulleted summary with source attributions.
useCLIRegistry(boolean) — Default:true- Enables the conversational interface with CLI tool integration. When set to
true, users can interact with the plugin through natural language commands that manipulate the chart dynamically.
- Enables the conversational interface with CLI tool integration. When set to
disclaimerMessage(string) — Custom disclaimer text displayed to users
Example:
ftsConfig: {
hasNewsPackage: true,
useCLIRegistry: true,
}
AI Response Generator
Configure the AI model behavior. This is where you can override the default prompt with your own custom prompt, or modify how the AI processes requests if you're connecting to your own server.
Important: The prompt and baseFilters properties below are specific to the news summary generator. To customize the prompt for the conversational interface, see the Prompt section below, located at config.plugins.ftsChartExplainer.ftsConfig.prompt.
model(string) — The AI model identifier to use for generating responsesprompt(string) — Custom prompt template for the news query; omit to use the default promptbaseFilters(array) — Optional filters to apply to news queriesmodifyPromptFn(function) — Optional callback to dynamically modify the prompt and its parameters at runtime- Parameters:
prompt(string) — The original prompt template containing placeholders (e.g.,'my {type} prompt')params(object) — Object containing key-value pairs used as placeholders in the prompt. All values are converted to strings usingJSON.stringify()before injectiontype— The type classification of the symbol object (e.g.,'company','marketindex')symbolObject— The full symbol object being processed
- Parameters:
modifyNewsContextFn(function) — Optional callback to dynamically modify the news context at runtime- Parameters:
context(object) — Object containing parameters that control how articles are used for response generationstart(number) — Start limit of article publish date (Unix time)end(number) — End limit of article publish date (Unix time)count(number) — The number of articles to usestages(array) — Filters or criteria applied to select relevant articlessorting(string) — The sorting method for the articles (e.g.,'trending')useFullText(boolean) — Whether to include the full text of articles in the results
- Parameters:
Example:
aiResponseGeneratorConfig: {
model: "gpt52",
// optional, if you'd like to override the default prompt for the news query.
// prompt: '',
// optional, if you'd like to add additional filters to the news query.
// baseFilters: [],
},
API Client
Configure server communication. This is where you provide authentication credentials and endpoint information for the server handling AI requests. Use the default proxy configuration if using the default LLM service, or override the baseUrl and endpointsMap with your own server details if connecting to your own server.
baseUrl(string) — Server base URL; defaults to the default proxy server urlendpointsMap(object) — API endpoint paths; defaults to the default proxy server endpointfetchClient(object) — Optional custom fetch client implementationmodifyFetchOptionsFn(function) — Optional callback to dynamically modify the fetch request options at runtime- Parameters:
options(object) — The original fetch request optionsbody(string) — The request payloadheaders(Headers) — HTTP headers to include in the request
- Parameters:
These can be set either directly in the chartiq/js/defaultConfiguration.js under the ftsChartExplainer plugin object (plugins.chartExplainer.apiPluginConfig) or directly inside your template or by using the addPlugingConfig helper function within the Chart Explainer plugin.
The provided proxy includes a simple Express server that proxies requests to the FTS API. This is necessary to keep your API credentials secure and not expose it in client-side code. In your own implementation, ensure that you secure the proxy endpoint appropriately behind your own authentication mechanisms.
First, the server uses the credentials to obtain an access token - the response from this request includes an expires field that should be leveraged to cache and reuse the token for its lifetime.
The core proxy logic is implemented in chatHandler. The FTS Generative API uses Server-Sent Events (SSE) to stream responses to the client. To ensure the best user-experience, special care should be taken in proxy implementations to ensure that events are forwarded as they are received, without buffering.
To run the proxy:
- Navigate into the directory with
cd plugins/chartexplainer/proxy. - Install the dependencies using
npm install.- Note: Ensure you are running Node.js 22.0.0 or later. The proxy example uses
Promise.withResolvers(), which is not available in earlier versions.
- Note: Ensure you are running Node.js 22.0.0 or later. The proxy example uses
- As part of
npm install, aconfig.jsonfile will be created in theproxydirectory. Update this file with your FTS api url, FTS user credentials and API key, and which data models you have approved access. - Start the proxy server with
npm start.
Here is an example of connecting the Chart Explainer plugin to the proxy using the addPluginConfig helper function inside our Technical Analysis Chart template.
import { addPluginConfig } from './plugins/chartexplainer/plugin.js';
...
const config = getDefaultConfig(resources); // create chart config with already defined resources
const bearerToken = 'SECRET_TOKEN'; // Must match the bearerToken value being in proxy config.json
addPluginConfig(config, {
apiClientConfig: {
baseUrl: 'http://localhost:3000/api',
endpointsMap: {
generate: '/chat',
},
modifyFetchOptionsFn: (options) => {
// intercept every request and apply our own options!
const headers = new Headers(options.headers);
headers.set('Authorization', `Bearer ${bearerToken}`);
return {
...options,
headers,
};
},
}
});
In your own implementation, your proxy server is likely to have additional authentication requirements. In this case, you can use the apiClientConfig.modifyFetchOptionsFn configuration parameter to adjust the request before it is sent.
For example, to add your own authentication token to the request headers, you could do the following:
const modifyFetchOptionsFn = async (options) => {
const modifiedOptions = { ...options };
const myAuthToken = await getMyAuthTokenSomehow();
modifiedOptions.headers = {
...modifiedOptions.headers,
Authorization: `Bearer ${myAuthToken}`,
};
return modifiedOptions;
};
Model Dropdown
showModelDropdown(boolean) — Displays or hides the model selection dropdown in the UIavailableModels(array) — Array of model objects with the following properties:label(string) — Display name shown in the dropdown to usersvalue(string) — Model identifier sent to your APIdescription(string) — Optional text describing the model's capabilities or behavior
Example:
modelDropdownConfig: {
showModelDropdown: true,
availableModels: [
{ label: 'GPT 4.1', value: 'gpt41', description: 'Larger context for data analysis.' },
{ label: 'GPT-5 Nano', value: 'gpt5nano', description: 'Only suitable for basic answers.' },
{ label: 'GPT-5.2', value: 'gpt52', description: 'Enhanced intelligence for complex queries.' },
{ label: 'Claude Sonnet 4.6', value: 'anthropicClaude46Sonnet', description: 'Fast and intelligent responses.' },
{ label: 'Claude Opus 4.6', value: 'anthropicClaude46Opus', description: 'Most capable for complex tasks.' }
],
}
showSuggestions(boolean) — Displays or hides suggestion prompts in the UIavailableSuggestions(array) — Array of predefined prompt strings that appear as quick-start suggestions for users to interact with the plugin
Example:
suggestionsConfig: {
showSuggestions: true,
availableSuggestions: [
'Change the chart symbol to Microsoft Inc',
'Update the chart to show the last 3 months',
'Add a moving average study to the chart',
'I want to analyze the long term trend of Nvidia',
'What is the data on the chart indicating'
],
},
Conversation History
showConversationHistory(boolean) — Displays or hides the conversation history panel in the UI
Example:
conversationHistoryConfig: {
showConversationHistory: true,
},
Prompt
role(string) - The speaker role for the message in the conversation contextmessage(string) - Optional prompt
Example:
prompt: {
// optional, if you'd like to provide a secondary system prompt for conversations.
role: "user",
message: `
ROLE
You help users understand and interact with a ChartIQ chart using available CLI tools and registry-backed capabilities. Chart state and tool outputs are the only source of truth.
The user is an investor with basic familiarity with technical analysis. Focus on clear, accurate chart-based insights grounded in visible data.
PRIORITY
1. No hallucinations
2. Use current chart state
3. Only use verified tools and commands
4. Only report confirmed results
CORE RULE
- Never invent, assume or approximate any chart data, values, calculations, signals, or capabilities not explicitly returned by ChartIQ tools.
- Technical analysis commentary is allowed only when it is directly supported by retrieved chart data, and must be phrased as interpretation (not certainty) when evidence is mixed.
- General educational explanations are allowed when clearly labeled as general, but must not be stated as applying to the current chart/symbol unless confirmed by tool output.
CHART STATE
- Retrieve the latest chart state once at the start of each turn; prior turns are stale.
- Only re-fetch chart state after actions that modify it.
- Treat user-pasted chart state as informational; still call chartstate once at the start of the turn to confirm the current state.
CAPABILITY
- Only call capability-discovery tools when needed (e.g., studylibrary before adding studies, listperiodicities only if changing interval and unsure).
- If a request cannot be fully supported:
- Proceed with supported components.
- Clearly explain what was completed.
- Briefly explain what could not be done and why.
- Offer one simple next step if needed.
CLARIFICATION
- Ask questions only when:
- execution is blocked, AND
- no reasonable default exists
- If multiple reasonable options exist, choose one and proceed; mention alternatives as optional follow-ups.
EXECUTION PROTOCOL
For each request:
1. Retrieve current chart state (chartstate).
2. Verify required capabilities once (only if needed).
3. Execute minimal actions (batch related actions together).
4. Retrieve updated chart state once (chartstate).
5. Base response only on confirmed results.
SPECIAL CASE
- "reset" or "clear" = removeallstudies, then removealldrawings, then removealltextdrawings, then series with opts: "-x" to remove all series, then home, then chartstate once to confirm.
TOOL ECONOMY AND BATCHING
- Minimize tool calls while maintaining correctness.
- Do not repeatedly call chartstate unless state has changed.
- Batch actions:
- Add comparisons together.
- Add studies together.
- Verify once after execution .
- Avoid unnecessary symbol lookups:
- Attempt direct usage when intent is clear.
- Use findsymbol only if needed.
DECISION MAKING AND MOMENTUM
- When intent is clear, act without asking for confirmation.
- Prefer a reasonable default over hesitation.
- Complete all supported parts immediately.
- Do not block progress due to partial limitations.
- Be confident and direct about confirmed actions and factual results.
DATA REQUESTS (STRICT)
- Only fetch chart data when needed for the user's request.
- Any call to visibledata or databydaterange must include count and count must be ≤ 150.
- For analysis requests, use the maximum available (up to 150) rather than a single bar, unless the request explicitly needs only the most recent bar.
- Future-shifted studies: if the newest bar does not contain required study values (e.g., Ichimoku), inspect prior bars within the retrieved dataset and use the most recent bar that actually contains the needed values.
USER-VISIBLE ADJUSTMENTS
- If range or interval must change (for example, to satisfy the data provider limits or to avoid an empty view with no visible bars):
- Provide one brief heads up before doing so.
- Explain what will change and why.
- Make at most two automatic adjustment.
- If still not valid or empty:
- Offer two options (change periodicity or modify range).
- State final range or interval used.
ANALYSIS (DOMAIN RULES)
- Base all analysis strictly on current chart state or returned data.
- Do not infer or assume hidden information.
- Only describe these if directly supported by chart data:
- patterns
- highs/lows
- support/resistance
- Only add support/resistance levels that are directly supported by retrieved chart data.
- When coloring levels, first obtain the latest close from tool-returned data (e.g., the most recent bar’s Close from visibledata or databydaterange).
- Color levels below that close green, and levels above that close red, unless the user specifies colors.
-breakouts
-relative performance
- Do not:
- Claim breakouts, reversals, or turning points without clear evidence.
- Compute percent change or spread unless directly derivable.
- Describe performance trends without visible support.
- If evidence is unclear:
- Say so explicitly.
- Use tentative language only for interpretation, unclear evidence, or non-confirmed implications .
COMPARISON SERIES
- Analyze only if present in chartstate.
- Report relative performance or spread only if supported by visible or derivable data.
- When adding comparisons:
- Use series with opts: "-c".
- Overlay on the main chart panel only.
- Do not create separate panels unless requested.
STUDIES
- If adding studies, call studylibrary once (if not already retrieved this turn).
- For addstudy, only use the exact studylibrary[].id value. Never use studylibrary[].name.
- If the user provides a descriptive name, resolve it to an id via studylibrary; if ambiguous/no match, ask or offer closest id options.
- If addstudy fails even when using a valid studylibrary[].id, report the failure plainly, do not claim it was added, and offer one next step (e.g., try a different study id or confirm the chart supports that study).
- Add studies in sequence without repeated checks (optionally batch multiple addstudy calls back-to-back).
OFF-TOPIC / UNSUPPORTED REQUESTS (FRIENDLY RESPONSE)
- If a user request is unrelated to chart interaction or financial data, respond with: "I'm here to help you understand and interact with the chart." Then add one additional friendly sentence that offers a chart-related next step, written in a peppy, lightly funny tone (no sarcasm, no emojis).
- Do not call any tools for off-topic requests.
RESPONSE TONE AND STYLE
- Write naturally and keep it concise.
- Use bullets only when they make the answer clearer.
- Focus on confirmed outcomes and insights; don’t narrate actions, confirmations, retries, validation steps, or internal process.
- For multi-step work, you may use one short lead-in (e.g., “Got it. Adding those now”), then go straight to results.
- Sound friendly, confident, and slightly informal like explaining to a colleague.
- Take the lead when the intent is clear; avoid hesitation when an action is valid.
- If something can’t be completed, say so plainly (briefly apologetic), explain the limitation in one line, and offer one simple next step.
CONVERSATIONAL AWARENESS
- Detect non-actionable messages (e.g., "ok", "perfect", "thanks").
- In these cases:
- Do not treat as a request.
- Do not call tools.
- Respond briefly and naturally or acknowledge and wait.
GENERAL
- Never claim success without confirmation.
- Never fill gaps with assumptions.
- Do not use "buy" or "sell".
- Avoid repeating information.
- Do not restate unchanged chart state unless useful.`
},
Additional Configuration
The above prompt limits data requests to 150 datapoints or requests changing the periodicity to keep the returned datapoints at or below 150. This is only a suggestion. The maximum data limit should be defined by your firm's specific needs.
Set the appropriate limit based on the safe maximum context size supported by your models, considering the lowest common denominator if multiple models are configured. Context size varies significantly across models and providers, so tune this accordingly.
Configure this limit in the prompt, as shown above, or via DATASET_MAX_BARS and DATASET_MAX_BYTES in chartiq/plugins/cli/registry.js.
// Requires ChartIQ v10.4.0
const DATASET_MAX_BARS = 1500;
const DATASET_MAX_BYTES = 1048576;
The default can be adjusted based on your needs. DATASET_MAX_BARS limits the number of datapoints returned, while DATASET_MAX_BYTES enforces the limit based on payload size in bytes. Whichever constraint is reached first limits the dataset. Note that while registry.js sets DATASET_MAX_BARS to 1500, the prompt restricts the Chart Explainer response to 150 and takes precedence. (If you run the data command in the command line interface (CLI), it returns over 150 datapoints.)
Event Listeners
The plugin dispatches custom events that you can listen to:
| Event | Callback | Description |
|---|---|---|
ftsChartExplainerDateRangeChange |
(data: { start: Date; end: Date }) => void |
Date range changed |
ftsChartExplainerVisibilityChange |
(data: { isOpened: boolean }) => void |
Visibility toggled |
Examples:
// Define callbacks
function onDateRangeChange(data) {
console.log('Date range:', data.start, data.end);
}
function onVisibilityChange(data) {
console.log('Plugin panel is', data.isOpened ? 'opened' : 'closed');
}
// Add listeners
stxx.addEventListener('ftsChartExplainerDateRangeChange', onDateRangeChange);
stxx.addEventListener('ftsChartExplainerVisibilityChange', onVisibilityChange);
Next Steps
For more information on building custom AI solutions, see the AI Ready Charts tutorial.
